#! /usr/bin/env python
import logging
import sys
from   time    import sleep
from   getpass import getpass
from   PyQt4 import QtGui, QtCore
import opentick
from   QtSignalEmitter import QtSignalEmitter

class ExchangeListClient( opentick.OTClient, QtSignalEmitter ):
    """OTClient subclass used to login to the OpenTick servers and obtain
    a list of exchanges.
    """
    def __init__( self ):
        opentick.OTClient.__init__(self)
        QtCore.QObject.__init__(self)
        return 
        
    def onLogin( self ):
        logging.info( "Login successful." )
        logging.info( "Requesting list of exchanges. " )
        #
        # Request a list of exchanges that can be accessed by the current
        # user. The onListExchanges() method will be called when this is received
        # from the server. We ignore the request_id returned by this method
        # in this sample code.
        #
        request_id = self.requestListExchanges()
        return 
        
    def onListExchanges( self, 
                         exchangeList ):
        logging.info( "List of exchanges received." )
        #
        # Do nothing if the exchange list is empty.
        #
        if ( None == exchangeList ) or ( 0 == len( exchangeList ) ):
            logging.warning( "Empty exchange list received from OTFeed server." )

        #
        # Display a formatted list of exchanges. A lot of magic numbers are
        # used here to format the list. These are chosen fairly arbitrarily
        # and through trial and error. They have little or no effect on the
        # logic of the program.
        #            
        format_string = "%-5s %-60s %-5s"
        print format_string % ( 'Code', 'Title', 'Available?' )
        print '---------------------------------------------------------------------------'
        #
        for i in range(0, len(exchangeList) ):
            print format_string % ( exchangeList[i].getCode(), \
                  exchangeList[i].getTitle(), \
                  exchangeList[i].getAvailable() )
        
        QtCore.QObject.emit( self.Emitter(),
                             QtCore.SIGNAL( 'onListExchanges' ),
                             exchangeList )
        return
    
    def onError( self, otError ):
        error_message = ( "[" + str(otError.getRequestId()) + ":" + str(otError.getCode() ) +
                          "] " + otError.getDescription() )

        logging.error( error_message )

        QtCore.QObject.emit( self.Emitter(),
                             QtCore.SIGNAL( 'onError' ),
                             error_message )
        return
#
# Dialog used to display OTFeed error messages.
#
class OtfeedErrorMessageHandler( QtCore.QObject ):
    def __init__( self ):
        super( QtCore.QObject, self ).__init__()
        self.message_box = QtGui.QMessageBox( "OTFeed Error",
                                              "",
                                              QtGui.QMessageBox.Critical,
                                              QtGui.QMessageBox.Ok,
                                              QtGui.QMessageBox.NoButton,
                                              QtGui.QMessageBox.NoButton )
        self.message_box.setWindowModality( QtCore.Qt.WindowModal )
        return
    def DisplayError( self, errorMessage ):
        self.message_box.setText( self.message_box.text() + "\n" + 
                                  errorMessage ) 
        if not self.message_box.isVisible():
            self.message_box.exec_()
            QtGui.qApp.quit()
        return
#
# Dialog used to query the login and password from the user.
#
class LoginDialog( QtGui.QDialog ):
    """Dialog to input username and password."""
    def __init__( self, parentWidget=None ):
        super( LoginDialog, self ).__init__( parentWidget )
        #
        self.__mLoginLineEdit = QtGui.QLineEdit( self )
        #
        self.__mPasswordLineEdit = QtGui.QLineEdit( self )
        self.__mPasswordLineEdit.setEchoMode( QtGui.QLineEdit.Password )
        #
        info_label = QtGui.QLabel( self.tr( "Please enter your OTFeed username and password." ) )
        #
        login_label = QtGui.QLabel( self.tr( "&Username:" ) )
        login_label.setBuddy( self.__mLoginLineEdit )
        #
        password_label = QtGui.QLabel( self.tr( "&Password:" ) )
        password_label.setBuddy( self.__mPasswordLineEdit )
        #
        grid_layout = QtGui.QGridLayout()
        grid_layout.addWidget( info_label,               0, 0, 1, 2 )
        grid_layout.addWidget( login_label,              1, 0 )
        grid_layout.addWidget( self.__mLoginLineEdit,    1, 1 )
        grid_layout.addWidget( password_label,           2, 0 )
        grid_layout.addWidget( self.__mPasswordLineEdit, 2, 1 )
        #
        button_box = QtGui.QDialogButtonBox( QtGui.QDialogButtonBox.Ok |
                                             QtGui.QDialogButtonBox.Cancel )
        self.connect( button_box,
                      QtCore.SIGNAL( 'accepted()' ),
                      self,
                      QtCore.SLOT( 'accept()' ) )
        self.connect( button_box,
                      QtCore.SIGNAL( 'rejected()' ),
                      self,
                      QtCore.SLOT( 'reject()' ) )
        #
        grid_layout.addWidget( button_box, 3, 0, 1, 2 )
        #
        self.setLayout( grid_layout )
        self.setWindowTitle( 'User Information' ) 
        return
    #
    def GetUserInfo( self ):
        return ( str(self.__mLoginLineEdit.text()),
                 str(self.__mPasswordLineEdit.text()) )
#
def main():
    #
    app = QtGui.QApplication( sys.argv ) 
    #
    # Create an instance of our OpenTick client and add a server host.
    #
    client = ExchangeListClient()
    client.addHost( "feed2.opentick.com", 10010 )
    #
    # Quit the application if either the exchange list is received or if
    # an error is encountered.
    #
    error_handler = OtfeedErrorMessageHandler() 
    QtCore.QObject.connect( client.Emitter(),
                            QtCore.SIGNAL( 'onListExchanges' ),
                            app,
                            QtCore.SLOT( 'quit()' ) )
    
    QtCore.QObject.connect( client.Emitter(),
                            QtCore.SIGNAL( 'onError' ),
                            error_handler.DisplayError )
    #
    # Display a dialog to let the user enter a login and password.
    #
    login_dialog = LoginDialog()
    if ( login_dialog.exec_() ):
        #
        # Extract the username and password from the dialog.
        #
        ( username, password ) = login_dialog.GetUserInfo()
        #
        # Make sure that the username and password are non-empty
        # strings.
        #
        if ( ( 0 == len( username ) ) or
             ( 0 == len( password ) ) ):
            sys.exit( QtGui.QMessageBox.critical( None,
                                                  "Invalid input",
                                                  "Empty username or password specified." ) )
        else:
            #
            # Initiate login process. 
            #
            logging.info( "Logging in as user '" + username + "'" )
            client.login( username, password )
            #
            # Start the application.
            #
            sys.exit( app.exec_() )
    else:
        logging.info( "Exchange list query cancelled by user." )
    #
    return
# -----------------------------------------------------------------
if __name__ == "__main__":
    try:
        logging.basicConfig( level  = logging.DEBUG,
                             format = '%(asctime)s %(levelname)s %(message)s' )
        main()
    except SystemExit:
        raise
    except:
        print \
"""An internal error occured.  Please report all the output from the program,
including the following traceback.
"""
        raise
